#!/usr/bin/env bash

###############################################################
# File Name: install_nacos_cluster.sh
# Version: V1.0
# Author: Brian Hsiung
# Created Time : 2025-01-21 9:37:53
# Description: Nacos集群部署脚本（使用外接MySQL）
##############################################################

# 调试跟踪（-e: 与错退出， -u: 捕获未定义的变量， -x: 调试模式，在执行每个命令前，将其打印到标准错误输出）
# set -eux

##############################################
# 全局配置区 (集中管理所有可配置参数)
##############################################
# Nacos版本
declare -r NACOS_VERSION="2.5.0"
# NACOS用户
declare -r NACOS_USER="nacos"
# nacos 安装目录 /opt 或 /data
declare -r NACOS_HOME="/data/nacos"
# JDK安装目录（固定的目录，不做调整）
declare -r JDK_HOME="/usr/local/java/jdk8"
# 阿里云OSS存储地址
declare -r OSS_BASE_URL="https://brianhsiung.oss-cn-hangzhou.aliyuncs.com"

# WEB端口
declare -r MAIN_PORT=8848
# 客户端gRPC端口 主端口+1000
declare -ri CLIENT_GRPC_PORT=$((MAIN_PORT + 1000))
# 服务端gRPC端口 主端口+1001
declare -ri SERVER_GRPC_PORT=$((MAIN_PORT + 1001))
# Jraft请求服务端口 主端口-1000
declare -ri JRAFT_PORT=$((MAIN_PORT - 1000))

# 使用外部MySQL的连接信息
declare -r MYSQL_ADDR="172.26.85.156"
declare -r MYSQL_PORT="3306"
declare -r MYSQL_USER="nacos_server"
declare -r MYSQL_PASSWD="Wm56lp!RiT@vht1KgpsGkE"
declare -r MYSQL_DB="nacos_server"

HOST_IP=$(hostname -I|awk 'NR==1{print $1}')

# Nacos集群节点
NACOS_NODES=("172.26.85.156" "172.26.85.157" "172.26.85.158")

WORK_DIR="$(cd "$(dirname "$0")" && pwd)"
cd "$WORK_DIR" || exit

### 日志函数
log_info()    { echo -e "\033[34m[INFO] $*\033[0m"; }
log_warn()    { echo -e "\033[33m[WARNING] $*\033[0m"; }
log_success() { echo -e "\033[32m[SUCCESS] $*\033[0m"; }
log_error()   { echo -e "\033[31m[ERROR] $*\033[0m"; exit 1; }

### 检查依赖
check_dependency() {
    local -a required_cmds=("wget" "tar" "groupadd" "useradd")
    local missing=()

    for cmd in "${required_cmds[@]}"; do
        if ! command -v "$cmd" &>/dev/null; then
            missing+=("$cmd")
        fi
    done

    ((${#missing[@]})) && log_error "缺失依赖命令: ${missing[*]}"
}

### 检查端口
check_ports() {

    local -a ports=("$MAIN_PORT" "$CLIENT_GRPC_PORT" "$SERVER_GRPC_PORT" "$JRAFT_PORT")
    local used_ports=()
    for port in "${ports[@]}"; do
        if ss -lntu | grep -q ":$port " &>/dev/null; then
            used_ports+=("$port")
        fi
    done

    ((${#used_ports[@]})) && log_error "端口 ${used_ports[*]} 被占用"

    if systemctl is-active firewalld &>/dev/null; then
        log_info "防火墙已开启，开放端口 $MAIN_PORT $CLIENT_GRPC_PORT $SERVER_GRPC_PORT $JRAFT_PORT"
        for port in "${ports[@]}"; do
            firewall-cmd --zone=public --add-port="$port"/tcp --permanent
        done
        firewall-cmd --reload
    fi

}

#############################################
# 设置目录权限（递归检查上级目录）
#############################################
set_parent_permissions() {
    ### 某些等保三服务器默认新建的的目录权限为700，文件权限为600，导致服务无法启动，所以需要遍历上级目录，将权限修改为755
    # 获取真实目录
    local target_dir
    target_dir=$(realpath "$1")
    # 获取上级目录
    local current_dir
    current_dir=$(dirname "$target_dir")

    # 做个判断，避免进入死循环
    [ -d "$target_dir" ] || log_error "目录不存在 [$target_dir]"

    # 排除敏感目录
    local -a exclude_dirs
    exclude_dirs=("/usr" "/usr/local" "/bin" "/sbin" "/lib" "/etc" "/dev" "/var" "/root" "/usr/share")

    while [[ "$current_dir" != "/" ]]; do

        # 排除敏感目录
        if printf '%s\n' "${exclude_dirs[@]}" | grep -qx "$current_dir"; then
            log_info "跳过系统保护目录: $current_dir"
            current_dir=$(dirname "$current_dir")
            continue
        fi

        # 跳过不存在的目录
        if [[ ! -d "$current_dir" ]]; then
            log_warn "警告: 目录不存在 [$current_dir]"
            current_dir=$(dirname "$current_dir")
            continue
        fi
        
        # 获取当前目录权限
        local perm_oct
        perm_oct=$(stat -c '%a' "$current_dir")
        log_info "目录 $current_dir 的原始权限为: $perm_oct"
        
        if [[ "$perm_oct" != "755" ]]; then
            log_info "正在设置权限: [755] -> $current_dir"
            if ! chmod 755 "$current_dir"; then
                log_error "错误: 无法设置目录权限 [$current_dir]"
            fi
        else
            log_success "权限正确 [755]: $current_dir"
        fi

        # 向上一级目录
        current_dir=$(dirname "$current_dir")
    done
}

# OpenJDK8U-jdk_x64_linux_hotspot_8u442b06.tar.gz
# OpenJDK8U-jdk_aarch64_linux_hotspot_8u442b06.tar.gz

# 以KylinOS-V10自带JDK启动的Nacos无法访问其dashboard
install_jdk() {

    local jdk_pkg
    local arch_type

    arch=$(uname -m)
    case "$arch" in
        aarch64|arm64)
            arch_type=aarch64 ;;
        amd64|x86_64)
            arch_type=x64 ;;
        *)
            log_error "不支持的架构: $arch" ;;
    esac

    jdk_pkg="OpenJDK8U-jdk_${arch_type}_linux_hotspot_8u442b06.tar.gz"

    if [ "$access_mode" = "external" ] && [ ! -f "$jdk_pkg" ]; then
        log_info "通过阿里云OSS下载JDK"
        wget -q "$OSS_BASE_URL/deploy/jdk/$jdk_pkg" || log_error "下载JDK失败"
    fi

    [ -f "$jdk_pkg" ] || log_error "此目录下不存在 $jdk_pkg"

    if [ ! -d "$JDK_HOME" ]; then
        mkdir -p "$JDK_HOME"
        tar -zxf "$jdk_pkg" -C "$JDK_HOME" --strip-components=1 --no-same-owner
        find "$JDK_HOME/bin" -type f -exec chmod 755 {} \;
    elif [ ! "$(ls -A $JDK_HOME 2>/dev/null)" ]; then
        log_info "如果目录存在且为空"
        tar -zxf "$jdk_pkg" -C "$JDK_HOME" --strip-components=1 --no-same-owner
        find "$JDK_HOME/bin" -type f -exec chmod 755 {} \;
    fi

    if [ ! -f "$JDK_HOME/bin/java" ] || [ ! -x "$JDK_HOME/bin/java" ]; then
        log_error "Java可执行文件不存在或无执行权限，JDK安装失败"
    fi

    # 等保三服务器上面默认新建的的目录权限为700，将目录权限修改为755
    log_info "设置目录权限"
    chmod 755 "$JDK_HOME"
    set_parent_permissions "$JDK_HOME"

    log_success "JDK安装成功"
    "$JDK_HOME/bin/java" -version
}

install_nacos() {
    ### 创建目录
    [ -d "$NACOS_HOME" ] || mkdir -p "$NACOS_HOME"

    if [ "$access_mode" = "external" ] && [ ! -f "nacos-server-$NACOS_VERSION.tar.gz" ]; then
        log_info "通过阿里云OSS下载nacos"
        wget -q "$OSS_BASE_URL/deploy/nacos/nacos-server-$NACOS_VERSION.tar.gz" || log_error "下载 nacos-server-$NACOS_VERSION.tar.gz 失败"
    fi

    ### 检查文件是否存在
    [ -f "nacos-server-$NACOS_VERSION.tar.gz" ] || log_error "此目录下不存在 nacos-server-$NACOS_VERSION.tar.gz"

    log_info "解压 nacos-server-$NACOS_VERSION.tar.gz"
    tar -zxf nacos-server-$NACOS_VERSION.tar.gz -C "$NACOS_HOME" --no-same-owner --strip-components=1
    find "$NACOS_HOME/bin" -type f -exec chmod 755 {} \;

    ### 配置文件
    log_info "添加集群配置文件"
    {   
        for node in "${NACOS_NODES[@]}"; do
            echo "$node:$MAIN_PORT"
        done
    } > "$NACOS_HOME/conf/cluster.conf"

    /bin/cp -f "$NACOS_HOME"/conf/application.properties "$NACOS_HOME"/conf/application.properties.bak

    # 添加配置文件，在执行脚本前修改数据库地址以及账号密码
    log_info "添加配置文件"
    cat > "$NACOS_HOME"/conf/application.properties << EOF
# 控制台访问上下文地址，默认为/nacos
server.servlet.contextPath=/nacos
server.error.include-message=ALWAYS
server.port=$MAIN_PORT
spring.sql.init.platform=mysql
db.num=1
# 修改数据库地址
db.url.0=jdbc:mysql://$MYSQL_ADDR:$MYSQL_PORT/$MYSQL_DB?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&us
eSSL=false&serverTimezone=UTC
# 修改数据库用户
db.user.0=$MYSQL_USER
# 修改数据库密码
db.password.0=$MYSQL_PASSWD
db.pool.config.connectionTimeout=30000
db.pool.config.validationTimeout=10000
db.pool.config.maximumPoolSize=20
db.pool.config.minimumIdle=2
nacos.config.push.maxRetryTime=50
server.tomcat.mbeanregistry.enabled=true
management.metrics.export.elastic.enabled=false
management.metrics.export.influx.enabled=false
server.tomcat.accesslog.enabled=true
server.tomcat.accesslog.maxDays=15
server.tomcat.accesslog.rotate=true
server.tomcat.accesslog.file-date-format=.yyyy-MM-dd-HH
server.tomcat.accesslog.pattern=%h %l %u %t "%r" %s %b %D %{User-Agent}i %{Request-Source}i
server.tomcat.basedir=file:$NACOS_HOME/tomcat
nacos.security.ignore.urls=/,/error,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-ui/public/**,/v1/auth/**,/v1/console/
health/**,/actuator/**,/v1/console/server/**
nacos.core.auth.system.type=nacos
nacos.core.auth.enabled=true
nacos.core.auth.caching.enabled=true
nacos.core.auth.enable.userAgentAuthWhite=false
nacos.core.auth.server.identity.key=nacos_auth_key
nacos.core.auth.server.identity.value=nacos_auth_value
nacos.core.auth.plugin.nacos.token.cache.enable=false
nacos.core.auth.plugin.nacos.token.expire.seconds=18000
# base64加密
nacos.core.auth.plugin.nacos.token.secret.key=SHpuTWg0Q1Q2ayFvdFZCOFdCNTBhIXZjQ3NLVDdaR2ZWRTc=
nacos.istio.mcp.server.enabled=false
EOF

    log_info "调整目录权限"
    chown "$NACOS_USER":"$NACOS_USER" -R "$NACOS_HOME"
    set_parent_permissions "$NACOS_HOME"
}

add_service() {
    log_info "添加Nacos服务"
    cat > /usr/lib/systemd/system/nacos.service << EOF
[Unit]
Description=Nacos
Documentation=https://nacos.io
Wants=network-online.target
After=network-online.target

[Service]

User=$NACOS_USER
Group=$NACOS_USER

Type=forking

# 设置JAVA_HOME
Environment="JAVA_HOME=$JDK_HOME"
# 可通过CUSTOM_NACOS_MEMORY设置内存
#Environment="CUSTOM_NACOS_MEMORY=-Xms1g -Xmx1g -Xmn512m"
ExecStart=/bin/bash $NACOS_HOME/bin/startup.sh -p embedded
ExecStop=/bin/bash $NACOS_HOME/bin/shutdown.sh

# Let systemd restart this service always
Restart=always

# 给服务分配独立的临时空间
PrivateTmp=true

[Install]
WantedBy=multi-user.target
EOF

    systemctl daemon-reload
    log_info "启动Nacos服务"
    systemctl enable --now nacos
}

main() {

    log_info "开始部署 Nacos 集群 (版本: ${NACOS_VERSION})"

    [[ "$EUID" -ne 0 ]] && log_error "请使用root账号执行此脚本！"

    if ! getent group "$NACOS_USER" >/dev/null 2>&1; then
        log_info "创建用户组: $NACOS_USER"
        groupadd -r "$NACOS_USER"
    fi

    if ! getent passwd "$NACOS_USER" >/dev/null 2>&1; then
        log_info "创建用户: $NACOS_USER"
        useradd -M -r -g "$NACOS_USER" "$NACOS_USER"
    fi

    if curl --output /dev/null --silent --head --fail --connect-timeout 5 --max-time 10 "$OSS_BASE_URL"/health; then
        access_mode='external'
    else
        log_info "使用离线安装模式"
    fi

    ### 检查依赖
    check_dependency

    ### 检查端口
    check_ports

    ### 安装jdk
    if [ -d "$JDK_HOME" ] && [ -f "$JDK_HOME/bin/java" ]; then
        log_info "JDK已安装，跳过安装步骤, 版本如下："
        "$JDK_HOME/bin/java" -version
    else
        install_jdk
    fi
    ### 安装nacos
    install_nacos
    ### 注册服务
    add_service
    log_success "Nacos集群部署完成\n访问地址: http://${HOST_IP}:${MAIN_PORT}/nacos"
}

main